package seedpod.kb2db;

/**
 * Copyright 2009 University of Washington This code is released under the GPL version 3 - with a copy of the license
 * included
 * 
 * @author Maya Hao Li
 */
import java.io.PrintWriter;

import seedpod.model.ProtegeValueType;
import seedpod.model.RdbCls;
import seedpod.model.RdbSlot;
import seedpod.model.SeedpodModel;
import seedpod.model.rdb.Attribute;
import seedpod.model.rdb.MetaRdbSlot;
import seedpod.model.rdb.RdbValueType;
import seedpod.model.rdb.SimpleForeignKey;

public class SlotMap implements Comparable<SlotMap> {
	public final static int MAP_SLOT_TO_ATTRIBUTE = 0;
	public final static int MAP_SLOT_TO_CLS = 2;
	public final static int MAP_SLOT_TO_FOREIGN_KEY = 1;
	public final static int MAP_SLOT_UNKNOWN = -1;

	// private final RdbSlot _relationSlot;
	private Attribute _attribute = null;
	private ClsMap _clsMap;
	private SimpleForeignKey _fk = null;
	private final ModelMap _map;
	private int _mapType = MAP_SLOT_UNKNOWN;
	private MetaRdbSlot _metaRdbSlot;
	private ClsMap _reifiedCls = null;
	private String _reifiedClsName = null;

	public SlotMap(ModelMap map, MetaRdbSlot metaSlot) {
		_map = map;
		_metaRdbSlot = metaSlot;
		_clsMap = _map.getClsMap( _metaRdbSlot.getDomain() );
		System.err.println( "    " + metaSlot.getDomain() );
		_clsMap.addSlotMap( this );
	}

	public SlotMap(ModelMap map, RdbSlot slot, Attribute att) {
		_map = map;

		_attribute = att;
		_mapType = MAP_SLOT_TO_ATTRIBUTE;

		// not all slot map have protege slots. e.g. thing attributes
		if ( slot != null )
			_clsMap = map.getClsMap( slot.getDomainCls().getName() );
		else _clsMap = map.getClsMap( att.getRelation().getName() );
		_clsMap.addSlotMap( this );

		_metaRdbSlot = MetaRdbSlot.create( slot, att );
	}

	public SlotMap(ModelMap map, RdbSlot slot, RdbCls cls) {
		_map = map;
		_reifiedCls = map.getClsMap( cls );
		_mapType = MAP_SLOT_TO_CLS;

		_clsMap = map.getClsMap( cls );
		_clsMap.addSlotMap( this );

		_metaRdbSlot = MetaRdbSlot.create( slot, cls );
	}

	public SlotMap(ModelMap map, RdbSlot slot, SimpleForeignKey fk) {
		_map = map;
		_fk = fk;
		_mapType = MAP_SLOT_TO_FOREIGN_KEY;

		_clsMap = map.getClsMap( slot.getDomainCls().getName() );
		_clsMap.addSlotMap( this );

		_metaRdbSlot = MetaRdbSlot.create( slot, fk );
	}

	public int compareTo( SlotMap o ) {
		return getViewSequence().compareTo( o.getViewSequence() );
	}

	public boolean equals( SlotMap osmap ) {
		return getName().equals( osmap.getName() );
	}

	public String getAllowedCls() {
		return _metaRdbSlot.getAllowedCls();
	}

	public String[] getAllowedValueArray() {
		String[] values = null;
		String valueStr = getAllowedValues();
		if ( valueStr != null ) values = valueStr.split( MetaRdbSlot.VALUE_DELIMITER );
		return values;
	}

	// TODO return String[] instead
	public String getAllowedValues() {
		return _metaRdbSlot.getAllowedValues();
	}

	public Attribute getAttribute() {
		return _attribute == null ? (Attribute)_fk : _attribute;
	}

	public Integer getAttributeID() {
		return _metaRdbSlot.getAtributeID();
	}

	public Integer getCardinalityMax() {
		return _metaRdbSlot.getCardinalityMax();
	}

	public Integer getCardinalityMin() {
		return _metaRdbSlot.getCardinalityMin();
	}

	public String getDefaultValue() {
		return _metaRdbSlot.getDefaultValue();
	}

	public String getDisplayName() {
		String displayName = _metaRdbSlot.getUserDefinedName();
		if ( displayName != null && displayName.trim().length() > 0 )
			return displayName;
		else return getName();
	}

	public String getDocumentation() {
		return _metaRdbSlot.getDocumentation();
	}

	public String getDomainCls() {
		return _metaRdbSlot.getDomain();
	}

	public ClsMap getDomainClsMap() {
		if ( _clsMap == null ) {
			String domainName = (String)_metaRdbSlot.getSlotInstanceData().get( MetaRdbSlot.DOMAIN );
			_clsMap = _map.getClsMap( domainName );
		}
		return _clsMap;
	}

	public SimpleForeignKey getForeignKey() {
		return _fk;
	}

	public String getFormWidget() {
		return _metaRdbSlot.getFormWidget();
	}

	public String getFormWidgetParam() {
		return _metaRdbSlot.getFormWidgetParam();
	}

	public Integer getFrameId() {
		return _metaRdbSlot.getFrameId();
	}

	public String getFreeExpression() {
		return _metaRdbSlot.getFreeExpression();
	}

	public int getMappingType() {
		return _mapType;
	}

	public ModelMap getModelMap() {
		return _map;
	}

	public String getName() {
		if ( _metaRdbSlot != null )
			return _metaRdbSlot.getName();
		else return "";
	}

	public Double getNumericMax() {
		return _metaRdbSlot.getNumericMax();
	}

	public Double getNumericMin() {
		return _metaRdbSlot.getNumericMin();
	}

	public ProtegeValueType getProtegeValueType() {
		return _metaRdbSlot.getProtegeValueType();
	}

	public String getRdbAttributeName() {
		return _metaRdbSlot.getRdbAttName();
	}

	public String getRdbClsName() {
		return _clsMap.getRdbClsName();
	}

	/**
	 * same as getName()
	 * 
	 * @return
	 */
	public String getRdbSlotName() {
		return _metaRdbSlot.getName();
	}

	// use getRdbTargetName() if you want to get the non-parenthesized version
	public String getRdbTarget() {
		return _metaRdbSlot.getRdbTarget();
	}
	
	public boolean isRdbTargetAttribute() {
		return getRdbTarget().startsWith( SeedpodModel.DEFAULT_META_RDB_SLOT );
	}
	
	public boolean isRdbTargetClass() {
		return getRdbTarget().startsWith( SeedpodModel.DEFAULT_META_RDB_CLASS );
	}

	public String getRdbTargetName() {
		String rdbTarget = getRdbTarget();
		return rdbTarget.length() == 0 ? "" : rdbTarget.substring( rdbTarget.indexOf( '(' ) + 1,
				rdbTarget.indexOf( ')' ) );
	}

	public Integer getRdbValueLength() {
		return _metaRdbSlot.getRdbValueLength();
	}

	public RdbValueType getRdbValueType() {
		return _metaRdbSlot.getRdbValueType();
	}

	public ClsMap getReifiedCls() {
		if ( _reifiedCls == null && _reifiedClsName != null ) {
			ClsMap rCls = _map.getClsMap( _reifiedClsName );
			_reifiedCls = rCls != null ? rCls : null;
		}

		return _reifiedCls;
	}

	public ClsMap getReifiedMetaCls() {
		return _reifiedCls;
	}

	public String getSlotInverse() {
		return _metaRdbSlot.getSlotInverse();
	}

	public String getSlotType() {
		return _metaRdbSlot.getSlotType();
	}

	public String getUnit() {
		return _metaRdbSlot.getUnit();
	}

	public String getUserDefinedName() {
		return _metaRdbSlot.getUserDefinedName();
	}

	public Float getViewSequence() {
		return _metaRdbSlot.getViewSequence();
	}

	public String getViewWidget() {
		return _metaRdbSlot.getViewWidget();
	}

	public String getViewWidgetParam() {
		return _metaRdbSlot.getViewWidgetParam();
	}

	@Override
	public int hashCode() {
		int hash = 1;
		hash = hash * 31 + _metaRdbSlot.getName().hashCode();
		hash = hash * 31 + _clsMap.hashCode();
		return hash;
	}

	public Boolean isAssociated() {
		return _metaRdbSlot.isAssociated();
	}

	public boolean isAttribute() {
		return _mapType == MAP_SLOT_TO_ATTRIBUTE || _mapType == MAP_SLOT_TO_FOREIGN_KEY;
	}

	public Boolean isIndexed() {
		return _metaRdbSlot.isIndexed();
	}

	public Boolean isMultiple() {
		return _metaRdbSlot.isMultiple();
	}

	public Boolean isNullable() {
		return _metaRdbSlot.getIsNullable();
	}

	// returns true if the SlotMap is implemented as an RDB_CLASS
	public boolean isRdbClassImpl() {
		return getRdbTarget().startsWith( SeedpodModel.RdbCls.RDB_CLASS );
	}

	// ProtegeValueType instance denotes a relationship.
	public boolean isRelationship() {
		return getProtegeValueType().equals( ProtegeValueType.Instance );
	}

	public Boolean isUnique() {
		return _metaRdbSlot.isUnique();
	}

	public void seralize( PrintWriter out ) {
		_metaRdbSlot.serializeInstance( out );
	}

	public void setForeignKey( SimpleForeignKey fk ) {
		_fk = fk;
		_mapType = MAP_SLOT_TO_FOREIGN_KEY;
	}

	public void setMapToCls( ClsMap mapToCls ) {
		_reifiedCls = mapToCls;

		// update the meta data info
		_metaRdbSlot.setRdbAttTarget( MetaRdbSlot.getTargetString( mapToCls ) );
		_metaRdbSlot.setIsAssociated( false );
		_metaRdbSlot.setRdbValueType( RdbValueType.RELATION );

		_mapType = MAP_SLOT_TO_CLS;
	}

	public void setMapToCls( String reifiedClsName ) {
		_reifiedClsName = reifiedClsName;
		_mapType = MAP_SLOT_TO_CLS;
	}

}
